﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace SPVisualDev.Services
{
    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("e707dcde-d1cd-11d2-bab9-00c04f8eceae")]

    internal interface IAssemblyCache
    {

        [PreserveSig()]

        int UninstallAssembly(uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, IntPtr pvReserved, out uint pulDisposition);

        [PreserveSig()]

        int QueryAssemblyInfo(uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, ref ASSEMBLY_INFO assemblyInfo);

        [PreserveSig()]

        int CreateAssemblyCacheItem(uint dwFlags, IntPtr pvReserved, out /*IAssemblyCacheItem*/IntPtr ppAsmItem, [MarshalAs(UnmanagedType.LPWStr)] String pszAssemblyName);

        [PreserveSig()]

        int CreateAssemblyScavenger(out object ppAsmScavenger);

        [PreserveSig()]

        int InstallAssembly(uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] string pszManifestFilePath, IntPtr pvReserved);

    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct ASSEMBLY_INFO
    {
        public int cbAssemblyInfo;
        public int assemblyFlags;
        public long assemblySizeInKB;
        [MarshalAs(UnmanagedType.LPWStr)]
        public String currentAssemblyPath;
        public int cchBuf;
    }

    public class FusionInstall
    {

        static internal int AddAssemblyToCache(string assembly)
        {

            IAssemblyCache ac = null;

            int hr = CreateAssemblyCache(out ac, 0);

            if (hr != 0)

                throw new Exception("Error adding assembly to GAC. HRESULT: " + hr.ToString());

            else

                return ac.InstallAssembly(0, assembly, (IntPtr)0);

        }

        static internal int RemoveAssemblyFromCache(string assembly)
        {

            IAssemblyCache ac = null;

            uint n;

            int hr = CreateAssemblyCache(out ac, 0);

            if (hr != 0)

                throw new Exception("Error removing assembly from GAC. HRESULT: " + hr.ToString());

            else

                return ac.UninstallAssembly(0, assembly, (IntPtr)0, out n);

        }

        static internal int QueryAssemblyInfo(String assemblyName, out ASSEMBLY_INFO assemblyInfo)
        {
            assemblyInfo = new ASSEMBLY_INFO();
            assemblyInfo.cchBuf = 512;
            assemblyInfo.currentAssemblyPath = new String('\0', assemblyInfo.cchBuf);

            IAssemblyCache ac = null;

            uint n;

            int hr = CreateAssemblyCache(out ac, 0);

            if (hr != 0)

                return hr;

            else

                return ac.QueryAssemblyInfo(1, assemblyName, ref assemblyInfo);
        }

        [DllImport("Fusion.dll", CharSet = CharSet.Auto)]
        internal static extern int CreateAssemblyCache(out IAssemblyCache ppAsmCache, uint dwReserved);

    }
}
